#include <malloc.h>
#include <stdlib.h>
#include <pthread.h>

#include "mem.h"
#include "log.h"
#include "core_conf.h"
#include "task.h"
#include "mem_hash.h"

extern struct core_conf core_conf;

/* fixme: can I transform size_t to unsigned long? */
void *s_malloc(size_t size)
{
	log_info("begin s_malloc.");
	void *ptr = NULL;
	if (core_conf.use_mm == 0) {
		ptr = malloc(size);

		if (ptr == NULL) {
			log_error("No enough mem.");
			return NULL;
		}

		return ptr;
	} else {
		pthread_t tid = pthread_self();
		struct task *task = get_task_by_tid(tid);
		if (task == NULL) {
			log_error("no such task."); /* if this happened...you should buy a lottery ticket...:) */
			return NULL;
		}

		ptr = malloc(size);

		if (ptr == NULL) {
			log_error("No enough mem.");
			return NULL;
		}
		/* fixme: should I lock task_manager->lock? */
		pthread_mutex_lock(&(task->mm->lock));

		int err = 0;
		err = hash_add_pointer_statist(&(task->mm->ps_hhead), ptr, size);
		if (err != 0) {
			log_error("hash_add_pointer_statist failed, err: %d", err);
			pthread_mutex_unlock(&(task->mm->lock));
			return ptr; /* fixme: Don't reutrn NULL, ignore it */
		}
		task->mm->total += (unsigned long)size;

		pthread_mutex_unlock(&(task->mm->lock));
		return ptr;
	}
}

void s_free(void *ptr)
{
	log_info("begin s_free.");
	if (ptr == NULL) {
		log_warning("Hey, you are freeing NULL pointer...");
		return;
	}

	if (core_conf.use_mm == 0) {
		free(ptr);
		/* ptr = NULL */
		return;
	} else {
		pthread_mutex_lock(&(core_conf.task_manager->lock));
		
		struct task *task = NULL;
		struct pointer_statist *ps = NULL;

		for (task = core_conf.task_manager->task_head; task != NULL; task = task->next) {
			/* TODO:lock task when you add remove-task feature. */
			pthread_mutex_lock(&(task->mm->lock));
			ps = hash_find_pointer_statist(task->mm->ps_hhead, ptr);
			if (ps != NULL) {
				break;
			}
			pthread_mutex_unlock(&(task->mm->lock));
		}

		if (task == NULL) {
			log_error("no such task.");
			pthread_mutex_unlock(&(core_conf.task_manager->lock));
			return;  /* task is remove, we shouldn't free it again. */
		}

		if (ps == NULL) {
			log_warning("cann't found ptr.");
			/* fixme: should I free ptr? */
			pthread_mutex_unlock(&(task->mm->lock));
			pthread_mutex_unlock(&(core_conf.task_manager->lock));

			return;
		}

		task->mm->total -= (unsigned long)(ps->size);
		
		int err = 0;
		err = hash_delete_pointer_statist(&(task->mm->ps_hhead), ps);
		if (err != 0) {
			log_error("hash_delete_pointer_statist failed, err: %d", err);
			free(ptr);
			/* ptr = NULL */
			pthread_mutex_unlock(&(task->mm->lock));
			pthread_mutex_unlock(&(core_conf.task_manager->lock));
			return;
		}
		free(ps);
		/* ps = NULL */

		free(ptr);
		/* ptr = NULL */

		pthread_mutex_unlock(&(task->mm->lock));
		pthread_mutex_unlock(&(core_conf.task_manager->lock));

		return;
	}
}

struct mm_struct *mm_struct_create(void)
{
	log_info("begin mm_struct_create.");

	struct mm_struct *mm_struct = NULL;
	mm_struct = (struct mm_struct *)malloc(sizeof(struct mm_struct)); 
	if (mm_struct == NULL) {
		log_error("No enough mem.");
		return NULL;
	}

	mm_struct->total = 0;
	mm_struct->ps_hhead = NULL;
	mm_struct->task = NULL;

	int err = 0;
	err = pthread_mutex_init(&(mm_struct->lock), NULL);
	if (err != 0) {
		log_error("Cann't init mm_struct->lock, err: %d", err);
		free(mm_struct);
		return NULL;
	}

	return mm_struct;
}
